package org.example.config;


import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.domain.po.ErpMenu;
import org.example.domain.po.ErpRole;
import org.example.domain.po.ErpUser;
import org.example.filter.TokenFilter;
import org.example.service.ErpMenuService;
import org.example.service.ErpRoleService;
import org.example.service.ErpUserService;
import org.example.utils.JwtUtil;
import org.example.utils.Result;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//enable
//disable
//伪链
@Configuration
public class SecurityConfig {
    @Resource
    TokenFilter tokenFilter;

    @Resource
    private ErpUserService erpUserService;
    
    @Resource
    private ErpMenuService erpMenuService;

    @Resource
    RedisTemplate<String, Object> redisTemplate;
    
    @Resource
    private ErpRoleService roleService;
    
    ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 创建spring的加密对象
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    public static void main(String[] args) {
//        System.out.println(new BCryptPasswordEncoder().encode("123456"));
        System.out.println(new BCryptPasswordEncoder().matches("123123", "$2a$10$KjvsLYMUaSpo1EdqiZLjteQo6Ga2CMwnul9sqMctY4DaaJrsXCoZm"));
    }
    
    /**
     * 自定义SQL进行查询账户信息，而不是从security默认表中去读取用户
     */
    @Bean
    public UserDetailsService userDetailsService() {
        return new UserDetailsService() {
            /**
             * 根据账户名加载用户信息
             * @throws UsernameNotFoundException
             */
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                QueryWrapper<ErpUser> userQuery = new QueryWrapper<>();
                userQuery.lambda().eq(ErpUser::getLoginName, username);
                ErpUser users = erpUserService.getOne(userQuery);
                if (users == null) {
                    throw new UsernameNotFoundException("账户名或密码错误");
                } else if (users.getStatus() == 2){
                    throw new UsernameNotFoundException("该账户已被封禁，请联系管理员");
                }
                List<ErpMenu> menus = null;
                ErpRole role = roleService.getRoleByUserId(users.getId());
                if (BeanUtil.isNotEmpty(role)){
                    if (!role.getName().equals("租户")){
                        menus = erpMenuService.findMenusByUserId(users.getId());
                    } else {
                        menus = erpMenuService.findMenusAdmin();
                    }
                }
                return new LoginUser(users, menus);
            }
        };
    }
    
    
    //cors()：允许跨域请求
    //disable(): 禁用
    //csrf().disable()：禁用伪链
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        //将整个系统的权限加载到security中
        //只查询F类的功能权限，排除菜单
        QueryWrapper<ErpMenu> menuQuery = new QueryWrapper<>();
        menuQuery.lambda().eq(ErpMenu::getType, "F");
        List<ErpMenu> menus = erpMenuService.list(menuQuery);
        //.successHandler() : 登录成功执行此函数
        //.failureHandler():登录失败，执行此函数
        http.csrf().disable().cors().and().formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/user/submitLogin").permitAll()
                // 处理登录成功














                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException{
                        response.setContentType("application/json;charset=utf-8");
                        response.setCharacterEncoding("utf-8");
                        // 拿到登录的凭证
                        // 用户信息和菜单信息(权限信息)
                        LoginUser user = (LoginUser) authentication.getPrincipal();
                        Map<String, Object> map = new HashMap<>();
                        map.put("id", user.getUsers().getId()); // 用户id
                        map.put("username", user.getUsername()); // 用户名
                        map.put("tenantId", user.getUsers().getTenantId()); // 租户id
                        map.put("loginName", user.getUsers().getLoginName()); // 登录名称
                        String token = JwtUtil.genToken(map);
                        String json = objectMapper.writeValueAsString(Result.success(200, "登录成功", token));
                        // 登录成功后，将当前登录的用户权限列表写入到redis
                        redisTemplate.opsForValue().set("login:" + user.getUsername(), user);
                        response.getWriter().print(json);
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.setCharacterEncoding("utf-8");
                        String json = objectMapper.writeValueAsString(Result.error(401, "账户或密码错误"));
                        response.getWriter().print(json);
                    }
                })
                .and().authorizeRequests().antMatchers("/user/getUserCountByUserName","/user/register", "/user/submitLogin")
                .permitAll()
        ;
        http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                response.setCharacterEncoding("utf-8");
                String json = objectMapper.writeValueAsString(Result.error(401, "你还未登录，请先登录"));
                response.getWriter().print(json);
            }
        }).accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                response.setCharacterEncoding("utf-8");
                String json = objectMapper.writeValueAsString(Result.error(403, "权限不足，请联系管理员"));
                response.getWriter().print(json);
            }
        });
        
        
        //加载上节课的权限列表
        menus.forEach(item->{
            if(item.getType().equals("F") && StringUtils.hasLength(item.getUrl())){
                try {
                    http.authorizeRequests().antMatchers(item.getUrl()).hasAuthority(item.getPerm());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        //所有请求都需要登录之后才能进入
        http.authorizeRequests().anyRequest().authenticated();
        //改变过滤器的验证顺序
        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

}
